From: Keir Fraser Date: Mon, 11 Feb 2008 09:46:21 +0000 (+0000) Subject: xentrace: Allow xentrace to handle >4G of trace data. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14317^2~62 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https:/%22bookmarks://%22Dat/%22http:/www.example.com/cgi/%22https:/%22bookmarks:/%22Dat?a=commitdiff_plain;h=d37035e9f9ee9dc1fddd3fc8fd541b91504b3d43;p=xen.git xentrace: Allow xentrace to handle >4G of trace data. It was previously assert'ing when it hit 4G. Also, because the trace buffer is not a power of 2 in size, using modulo arithmetic to address the buffer does not work when the index wraps around 2^32. This patch fixes both issues, and as a side effect, removes all integer division from the hypervisor side of the trace mechanism. Signed-off-by: Michael A Fetterman --- diff --git a/tools/xentrace/xentrace.c b/tools/xentrace/xentrace.c index 26415bdb7d..bd276cc3ed 100644 --- a/tools/xentrace/xentrace.c +++ b/tools/xentrace/xentrace.c @@ -362,9 +362,18 @@ int monitor_tbufs(int outfd) if ( cons == prod ) continue; - assert(prod > cons); + assert(cons < 2*data_size); + assert(prod < 2*data_size); + + // NB: if (prod 0); + assert(window_size <= data_size); - window_size = prod - cons; start_offset = cons % data_size; end_offset = prod % data_size; diff --git a/xen/common/trace.c b/xen/common/trace.c index d2f8fceeae..863816b9ee 100644 --- a/xen/common/trace.c +++ b/xen/common/trace.c @@ -239,14 +239,46 @@ static inline int calc_rec_size(int cycles, int extra) return rec_size; } +static inline int calc_unconsumed_bytes(struct t_buf *buf) +{ + int x = buf->prod - buf->cons; + if ( x < 0 ) + x += 2*data_size; + + ASSERT(x >= 0); + ASSERT(x <= data_size); + + return x; +} + static inline int calc_bytes_to_wrap(struct t_buf *buf) { - return data_size - (buf->prod % data_size); + int x = data_size - buf->prod; + if ( x <= 0 ) + x += data_size; + + ASSERT(x > 0); + ASSERT(x <= data_size); + + return x; } -static inline unsigned calc_bytes_avail(struct t_buf *buf) +static inline int calc_bytes_avail(struct t_buf *buf) { - return data_size - (buf->prod - buf->cons); + return data_size - calc_unconsumed_bytes(buf); +} + +static inline struct t_rec * +next_record(struct t_buf *buf) +{ + int x = buf->prod; + if ( x >= data_size ) + x -= data_size; + + ASSERT(x >= 0); + ASSERT(x < data_size); + + return (struct t_rec *)&this_cpu(t_data)[x]; } static inline int __insert_record(struct t_buf *buf, @@ -260,24 +292,25 @@ static inline int __insert_record(struct t_buf *buf, unsigned char *dst; unsigned long extra_word = extra/sizeof(u32); int local_rec_size = calc_rec_size(cycles, extra); + uint32_t next; BUG_ON(local_rec_size != rec_size); + BUG_ON(extra & 3); /* Double-check once more that we have enough space. * Don't bugcheck here, in case the userland tool is doing * something stupid. */ if ( calc_bytes_avail(buf) < rec_size ) { - printk("%s: %u bytes left (%u - (%u - %u)) recsize %u.\n", + printk("%s: %u bytes left (%u - ((%u - %u) %% %u) recsize %u.\n", __func__, - data_size - (buf->prod - buf->cons), - data_size, - buf->prod, buf->cons, rec_size); + calc_bytes_avail(buf), + data_size, buf->prod, buf->cons, data_size, rec_size); return 0; } rmb(); - rec = (struct t_rec *)&this_cpu(t_data)[buf->prod % data_size]; + rec = next_record(buf); rec->event = event; rec->extra_u32 = extra_word; dst = (unsigned char *)rec->u.nocycles.extra_u32; @@ -293,7 +326,13 @@ static inline int __insert_record(struct t_buf *buf, memcpy(dst, extra_data, extra); wmb(); - buf->prod += rec_size; + + next = buf->prod + rec_size; + if ( next >= 2*data_size ) + next -= 2*data_size; + ASSERT(next >= 0); + ASSERT(next < 2*data_size); + buf->prod = next; return rec_size; } @@ -395,7 +434,7 @@ void __trace_var(u32 event, int cycles, int extra, unsigned char *extra_data) local_irq_save(flags); - started_below_highwater = ((buf->prod - buf->cons) < t_buf_highwater); + started_below_highwater = (calc_unconsumed_bytes(buf) < t_buf_highwater); /* Calculate the record size */ rec_size = calc_rec_size(cycles, extra); @@ -413,10 +452,6 @@ void __trace_var(u32 event, int cycles, int extra, unsigned char *extra_data) total_size = 0; /* First, check to see if we need to include a lost_record. - * - * calc_bytes_to_wrap() involves integer division, which we'd like to - * avoid if we can. So do the math, check it in debug versions, and - * do a final check always if we happen to write a record. */ if ( this_cpu(lost_records) ) { @@ -477,7 +512,7 @@ void __trace_var(u32 event, int cycles, int extra, unsigned char *extra_data) /* Notify trace buffer consumer that we've crossed the high water mark. */ if ( started_below_highwater && - ((buf->prod - buf->cons) >= t_buf_highwater) ) + (calc_unconsumed_bytes(buf) >= t_buf_highwater) ) raise_softirq(TRACE_SOFTIRQ); } diff --git a/xen/include/public/trace.h b/xen/include/public/trace.h index 4cbb896342..21fe15ffb9 100644 --- a/xen/include/public/trace.h +++ b/xen/include/public/trace.h @@ -141,6 +141,14 @@ struct t_rec { * field, indexes into an array of struct t_rec's. */ struct t_buf { + /* Assume the data buffer size is X. X is generally not a power of 2. + * CONS and PROD are incremented modulo (2*X): + * 0 <= cons < 2*X + * 0 <= prod < 2*X + * This is done because addition modulo X breaks at 2^32 when X is not a + * power of 2: + * (((2^32 - 1) % X) + 1) % X != (2^32) % X + */ uint32_t cons; /* Offset of next item to be consumed by control tools. */ uint32_t prod; /* Offset of next item to be produced by Xen. */ /* Records follow immediately after the meta-data header. */